{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "1505a6b9-2bb1-4640-bc03-8d4cfadd5164",
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import torch\n",
    "from torch import nn\n",
    "from d2l import torch as d2l\n",
    "\n",
    "def try_gpu(i=0):  #@save\n",
    "    \"\"\"如果存在，则返回gpu(i)，否则返回cpu()。\"\"\"\n",
    "    if torch.cuda.device_count() >= i + 1:\n",
    "        return torch.device(f'cuda:{i}')\n",
    "    return torch.device('cpu')\n",
    "\n",
    "try_gpu()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "3ed649b3-9514-4228-8c9a-ec55a88ea666",
   "metadata": {},
   "outputs": [],
   "source": [
    "train_data = pd.read_csv('data/train.csv')\n",
    "test_data = pd.read_csv('data/test.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "cf665062-e5ea-4113-95f0-59916537a5dd",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(47439, 41)\n",
      "(31626, 40)\n"
     ]
    }
   ],
   "source": [
    "print(train_data.shape)\n",
    "print(test_data.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "52a4f1d4-a850-4a0f-9c6b-b8368a962e42",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "   Id            Address  Sold Price  \\\n",
      "0   0        540 Pine Ln   3825000.0   \n",
      "1   1     1727 W 67th St    505000.0   \n",
      "2   2     28093 Pine Ave    140000.0   \n",
      "3   3  10750 Braddock Dr   1775000.0   \n",
      "\n",
      "                                             Summary         City    Zip State  \n",
      "0  540 Pine Ln, Los Altos, CA 94022 is a single f...    Los Altos  94022    CA  \n",
      "1  HURRY, HURRY.......Great house 3 bed and 2 bat...  Los Angeles  90047    CA  \n",
      "2  'THE PERFECT CABIN TO FLIP!  Strawberry deligh...   Strawberry  95375    CA  \n",
      "3  Rare 2-story Gated 5 bedroom Modern Mediterran...  Culver City  90230    CA  \n",
      "Index(['Id', 'Address', 'Sold Price', 'Summary', 'Type', 'Year built',\n",
      "       'Heating', 'Cooling', 'Parking', 'Lot', 'Bedrooms', 'Bathrooms',\n",
      "       'Full bathrooms', 'Total interior livable area', 'Total spaces',\n",
      "       'Garage spaces', 'Region', 'Elementary School',\n",
      "       'Elementary School Score', 'Elementary School Distance',\n",
      "       'Middle School', 'Middle School Score', 'Middle School Distance',\n",
      "       'High School', 'High School Score', 'High School Distance', 'Flooring',\n",
      "       'Heating features', 'Cooling features', 'Appliances included',\n",
      "       'Laundry features', 'Parking features', 'Tax assessed value',\n",
      "       'Annual tax amount', 'Listed On', 'Listed Price', 'Last Sold On',\n",
      "       'Last Sold Price', 'City', 'Zip', 'State'],\n",
      "      dtype='object')\n"
     ]
    }
   ],
   "source": [
    "print(train_data.iloc[0:4, [0, 1, 2, 3, -3, -2, -1]])\n",
    "print(train_data.columns)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a28fa2b8-c17f-45f9-aecd-669751e9e915",
   "metadata": {},
   "source": [
    "'Id', \n",
    "'Address', 地址\n",
    "'Sold Price', 真实售价\n",
    "'Summary',  卖方广告\n",
    "'Type',  类型 singleFamily 别墅 condo 连体别墅\n",
    "'Year built',  建设时间\n",
    "'Heating',  供暖\n",
    "'Cooling',  空调情况\n",
    "'Parking',  车库\n",
    "'Lot',      面积大小\n",
    "'Bedrooms', 卧室\n",
    "'Bathrooms', 洗手间（不能洗澡）\n",
    "'Full bathrooms',  全洗手间（可以洗澡）\n",
    "'Total interior livable area',  居住面积\n",
    "'Total spaces',  总面积\n",
    "'Garage spaces',  车库空间\n",
    "'Region',  区域\n",
    "'Elementary School', 小学  'Elementary School Score',  小学排名   'Elementary School Distance', 学校距离\n",
    "'Middle School', 'Middle School Score', 'Middle School Distance',\n",
    "'High School', 'High School Score', 'High School Distance', \n",
    "'Flooring', 地板材料\n",
    "'Heating features', 'Cooling features',  供暖特点、制冷特点\n",
    "'Appliances included', 包括电器\n",
    "'Laundry features', 'Parking features',  洗衣机特色、车库特色\n",
    "'Tax assessed value', 纳税评估值\n",
    "'Annual tax amount',  年税额\n",
    "'Listed On', 'Listed Price',  卖家上架时间和标价\n",
    "'Last Sold On', 'Last Sold Price',  上次售出交易时间和价格\n",
    "'City', 'Zip' 邮编, 'State' 州"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "440d5d83-aa91-4d68-a8e0-07fbc5117d14",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(79065, 37)\n",
      "<class 'pandas.core.frame.DataFrame'>\n",
      "Int64Index: 79065 entries, 0 to 31625\n",
      "Data columns (total 37 columns):\n",
      " #   Column                       Non-Null Count  Dtype  \n",
      "---  ------                       --------------  -----  \n",
      " 0   Type                         79065 non-null  object \n",
      " 1   Year built                   77123 non-null  float64\n",
      " 2   Heating                      67552 non-null  object \n",
      " 3   Cooling                      63956 non-null  object \n",
      " 4   Parking                      77389 non-null  object \n",
      " 5   Lot                          56076 non-null  float64\n",
      " 6   Bedrooms                     74467 non-null  object \n",
      " 7   Bathrooms                    73655 non-null  float64\n",
      " 8   Full bathrooms               66137 non-null  float64\n",
      " 9   Total interior livable area  75187 non-null  float64\n",
      " 10  Total spaces                 77398 non-null  float64\n",
      " 11  Garage spaces                77398 non-null  float64\n",
      " 12  Region                       79063 non-null  object \n",
      " 13  Elementary School            70572 non-null  object \n",
      " 14  Elementary School Score      70330 non-null  float64\n",
      " 15  Elementary School Distance   70572 non-null  float64\n",
      " 16  Middle School                50788 non-null  object \n",
      " 17  Middle School Score          50786 non-null  float64\n",
      " 18  Middle School Distance       50788 non-null  float64\n",
      " 19  High School                  71891 non-null  object \n",
      " 20  High School Score            71281 non-null  float64\n",
      " 21  High School Distance         71890 non-null  float64\n",
      " 22  Flooring                     57138 non-null  object \n",
      " 23  Heating features             66517 non-null  object \n",
      " 24  Cooling features             62432 non-null  object \n",
      " 25  Appliances included          55716 non-null  object \n",
      " 26  Laundry features             59083 non-null  object \n",
      " 27  Parking features             72437 non-null  object \n",
      " 28  Tax assessed value           72742 non-null  float64\n",
      " 29  Annual tax amount            71856 non-null  float64\n",
      " 30  Listed On                    79065 non-null  object \n",
      " 31  Listed Price                 79065 non-null  float64\n",
      " 32  Last Sold On                 49520 non-null  object \n",
      " 33  Last Sold Price              49520 non-null  float64\n",
      " 34  City                         79065 non-null  object \n",
      " 35  Zip                          79065 non-null  int64  \n",
      " 36  State                        79065 non-null  object \n",
      "dtypes: float64(17), int64(1), object(19)\n",
      "memory usage: 22.9+ MB\n"
     ]
    }
   ],
   "source": [
    "all_features = pd.concat((train_data.drop(columns=['Id', 'Sold Price']), test_data.iloc[:, 1:]))\n",
    "all_features = all_features.drop(columns=['Summary', 'Address'])\n",
    "print(all_features.shape)\n",
    "all_features.info()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "5e217f31-deaf-4131-899b-4b721d3acdda",
   "metadata": {},
   "outputs": [],
   "source": [
    "numeric_features = all_features.dtypes[all_features.dtypes != 'object'].index\n",
    "all_features[numeric_features] = all_features[numeric_features].apply(\n",
    "    lambda x: (x - x.mean()) / (x.std()))\n",
    "\n",
    "all_features[numeric_features] = all_features[numeric_features].fillna(0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "1cb24267-6417-409a-90d4-b79c3a0c6d72",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'pandas.core.frame.DataFrame'>\n",
      "Int64Index: 79065 entries, 0 to 31625\n",
      "Data columns (total 18 columns):\n",
      " #   Column                       Non-Null Count  Dtype  \n",
      "---  ------                       --------------  -----  \n",
      " 0   Year built                   79065 non-null  float64\n",
      " 1   Lot                          79065 non-null  float64\n",
      " 2   Bathrooms                    79065 non-null  float64\n",
      " 3   Full bathrooms               79065 non-null  float64\n",
      " 4   Total interior livable area  79065 non-null  float64\n",
      " 5   Total spaces                 79065 non-null  float64\n",
      " 6   Garage spaces                79065 non-null  float64\n",
      " 7   Elementary School Score      79065 non-null  float64\n",
      " 8   Elementary School Distance   79065 non-null  float64\n",
      " 9   Middle School Score          79065 non-null  float64\n",
      " 10  Middle School Distance       79065 non-null  float64\n",
      " 11  High School Score            79065 non-null  float64\n",
      " 12  High School Distance         79065 non-null  float64\n",
      " 13  Tax assessed value           79065 non-null  float64\n",
      " 14  Annual tax amount            79065 non-null  float64\n",
      " 15  Listed Price                 79065 non-null  float64\n",
      " 16  Last Sold Price              79065 non-null  float64\n",
      " 17  Zip                          79065 non-null  float64\n",
      "dtypes: float64(18)\n",
      "memory usage: 11.5 MB\n"
     ]
    }
   ],
   "source": [
    "# 'Dummy_na=True' \n",
    "#all_features = pd.get_dummies(all_features, dummy_na=True)\n",
    "#all_features.shape\n",
    "all_features[numeric_features].info()\n",
    "all_features = all_features[numeric_features]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "5d7bf5db-6f40-4786-b390-7c18375b782f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 完成数据准备最后一步\n",
    "n_train = train_data.shape[0]\n",
    "train_features = torch.tensor(all_features[:n_train].values, dtype = torch.float32)\n",
    "test_features = torch.tensor(all_features[n_train:].values, dtype = torch.float32)\n",
    "train_labels = torch.tensor(train_data['Sold Price'].values.reshape(-1, 1), dtype = torch.float32)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "dc4f60b0-5b86-465a-94de-0416842b4396",
   "metadata": {},
   "outputs": [],
   "source": [
    "loss = nn.MSELoss()\n",
    "in_features = train_features.shape[1]\n",
    "\n",
    "def get_net():\n",
    "    net = nn.Sequential(nn.Linear(in_features, 1))\n",
    "    return net"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "18290393-7822-4fe3-98e3-3c57e2675c1e",
   "metadata": {},
   "outputs": [],
   "source": [
    "def log_rmse(net, features, labels):\n",
    "    # 为了在取对数时进一步稳定该值，将小于1的值设置为1\n",
    "    clipped_preds = torch.clamp(net(features), 1, float('inf'))\n",
    "    rmse = torch.sqrt(loss(torch.log(clipped_preds), torch.log(labels)))\n",
    "    return rmse.item()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "fdc0e2dc-20bd-4eaa-982a-f29845313eb0",
   "metadata": {},
   "outputs": [],
   "source": [
    "def train(net, train_features, train_labels, test_features, test_labels, \n",
    "          num_epochs, learning_rate, weight_decay, batch_size):\n",
    "    use_gpu = torch.cuda.is_available()\n",
    "    loss_f = loss\n",
    "#     if(!use_gpu):\n",
    "#         loss_f = loss.cuda()\n",
    "#         net = net.to(device=try_gpu())\n",
    "#         train_features = train_features.cuda()\n",
    "#         train_labels = train_labels.cuda()\n",
    "#         test_features = test_features.cuda()\n",
    "#         test_labels = test_labels.cuda()\n",
    "        \n",
    "        \n",
    "    train_ls, test_ls = [], []\n",
    "    train_iter = d2l.load_array((train_features, train_labels), batch_size)\n",
    "    \n",
    "    # adam优化算法\n",
    "    optimizer = torch.optim.Adam(net.parameters(), lr = learning_rate, weight_decay = weight_decay)\n",
    "    \n",
    "    for epoch in range(num_epochs):\n",
    "        for X, y in train_iter:\n",
    "            optimizer.zero_grad()\n",
    "            l = loss_f(net(X), y)\n",
    "            l.backward()\n",
    "            optimizer.step()\n",
    "        train_ls.append(log_rmse(net, train_features, train_labels))\n",
    "        if test_labels is not None:\n",
    "            test_ls.append(log_rmse(net, test_features, test_labels))\n",
    "    return train_ls, test_ls"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "1cedfcfd-e7fd-4083-af38-9e9b854dfa8a",
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_k_fold_data(k, i, X, y):\n",
    "    assert k > 1\n",
    "    fold_size = X.shape[0] // k\n",
    "    X_train, y_train = None, None\n",
    "    for j in range(k):\n",
    "        idx = slice(j * fold_size, (j + 1) * fold_size)\n",
    "        X_part, y_part = X[idx, :], y[idx]\n",
    "        if j == i:\n",
    "            X_valid, y_valid = X_part, y_part\n",
    "        elif X_train is None:\n",
    "            X_train, y_train = X_part, y_part\n",
    "        else:\n",
    "            X_train = torch.cat([X_train, X_part], 0)\n",
    "            y_train = torch.cat([y_train, y_part], 0)\n",
    "    return X_train, y_train, X_valid, y_valid"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "63253fe3-4e54-472a-a045-346f3ebd3082",
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n",
    "\n",
    "def k_fold(k, X_train, y_train, num_epochs, learning_rate, weight_decay,\n",
    "           batch_size):\n",
    "    train_l_sum, valid_l_sum = 0, 0\n",
    "    # 训练K次\n",
    "    for i in range(k):\n",
    "        time_start=time.time()\n",
    "        # 返回一次K折的数据，i是验证集的index ，返回训练集和验证集\n",
    "        data = get_k_fold_data(k, i, X_train, y_train)\n",
    "        net = get_net()\n",
    "        train_ls, valid_ls = train(net, *data, num_epochs, learning_rate,\n",
    "                                   weight_decay, batch_size)\n",
    "        train_l_sum += train_ls[-1]\n",
    "        valid_l_sum += valid_ls[-1]\n",
    "        if i == 0:\n",
    "            d2l.plot(list(range(1, num_epochs + 1)), [train_ls, valid_ls],\n",
    "                     xlabel='epoch', ylabel='rmse', xlim=[1, num_epochs],\n",
    "                     legend=['train', 'valid'], yscale='log')\n",
    "            \n",
    "\n",
    "        time_end=time.time()\n",
    "        print(f'fold {i + 1}, train log rmse {float(train_ls[-1]):f}, '\n",
    "              f'valid log rmse {float(valid_ls[-1]):f} , Time used: {time_end-time_start:.2f} s')\n",
    "    return train_l_sum / k, valid_l_sum / k"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4f1d869c-0b05-4ebe-97f8-04b9a7ae04e6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "fold 1, train log rmse 1.812279, valid log rmse 1.649214 , Time used: 166.55 s\n",
      "fold 2, train log rmse 1.870630, valid log rmse 1.674335 , Time used: 166.31 s\n"
     ]
    }
   ],
   "source": [
    "k = 5\n",
    "num_epochs = 400\n",
    "learning_rate = 5\n",
    "weight_decay = 0\n",
    "batch_size = 64\n",
    "train_l, valid_l = k_fold(k, train_features, train_labels, num_epochs, learning_rate,\n",
    "                          weight_decay, batch_size)\n",
    "print(f'{k}-折验证: 平均训练log rmse: {float(train_l):f}, '\n",
    "      f'平均验证log rmse: {float(valid_l):f}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "34e02b8c-e902-4750-927c-ce4de06e83f8",
   "metadata": {},
   "outputs": [],
   "source": [
    "def train_and_pred(train_features, test_feature, train_labels, test_data,\n",
    "                   num_epochs, lr, weight_decay, batch_size):\n",
    "    net = get_net()\n",
    "    train_ls, _ = train(net, train_features, train_labels, None, None,\n",
    "                        num_epochs, lr, weight_decay, batch_size)\n",
    "    d2l.plot(np.arange(1, num_epochs + 1), [train_ls], xlabel='epoch',\n",
    "             ylabel='log rmse', xlim=[1, num_epochs], yscale='log')\n",
    "    print(f'train log rmse {float(train_ls[-1]):f}')\n",
    "    # 将网络应用于测试集。\n",
    "    preds = net(test_features).detach().numpy()\n",
    "    # 将其重新格式化以导出到Kaggle\n",
    "    test_data['SalePrice'] = pd.Series(preds.reshape(1, -1)[0])\n",
    "    submission = pd.concat([test_data['Id'], test_data['SalePrice']], axis=1)\n",
    "    submission.to_csv('submission.csv', index=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3463e762-5c2a-45df-a7ac-d93e055d0875",
   "metadata": {},
   "outputs": [],
   "source": [
    "train_and_pred(train_features, test_features, train_labels, test_data,\n",
    "               num_epochs, lr, weight_decay, batch_size)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
